Newer
Older
client_dwatch / docs / handoff / 指示書BG_予定外タスクを集計と表示に含める_20261029.md

指示書BG — 予定外タスクを集計と表示に含める(2026-10-29)

担当: Claude Code(サブエージェント3本)。監督がレビューする。 前提: develop の f134ac9 以降。予定外の判定は lib/db/timetable-link.ts の scheduledTimetableId()(clean_timetable_id が 0 か NULL なら予定外)。


0. ユーザーの指示(2026-10-29。原文)

  • 「エラー一覧以外も、予定外タスクを計算して出すようにしてください / ただし、プリチェックは除いてです」
  • 監督の案(画面ごとの扱い)への回答:
    • 「清掃詳細ログは事前チェックは出す必要なないです」
    • 「ダッシュボード、連続日数・要レスキュー、清掃エリア、エラー内訳はそれでOK」
    • 「割合はA案で」(A = 予定外タスクを「予定1回・出発1回」として予定回数にも足す)
    • 「サマリは変えて貰う予定です」(確定期間は TeamWorks のサマリをそのまま読むので、アプリ側は変えない)
    • 「上記の計算や表示は全て事前チェックは抜いてください」

監督の解釈(ユーザーに報告済みとすること)

  • 予定外タスクには事前チェックの回が無い → 予定外の行の事前チェック2列は null(画面「-」)。予定のある行の事前チェック列は今のまま
  • 事前チェックの回(precheck ≠ 0)に紐づくエラー・行は、どの集計にも入れない(今と同じ)
  • ステージングの実データ(2026-10-29・先方投入): 清掃結果15件のうち9件、エラー18件のうち13件が予定外

1. 予定外タスクの「1回」の定義

項目 予定のある清掃 予定外タスク
起点 予定の行(clean_timetables / 履歴の precheck = 0) clean_task_reports の行(scheduledTimetableId() が null)
日付・期間の振り分け 予定の start_at(JST) 清掃結果の started_at(JST)。★予定外は実際に始めた時刻(CLAUDE.md §4.7)★
出発 清掃結果の行があるか ★常に出発済み★(行があるので)
未実施・結果待ち ありうる ★ありえない★
1回ごとの完了率 runCompletionRate() 同じ runCompletionRate()
◎○△× 完了率で判定 同じ
事前チェック 1回目・2回目の列 ★無し(null)★
エラー 予定の id に紐づく robot_error_reports clean_timetable_id が 0 / NULL の robot_error_reports。期間は errored_at ?? created_at(detectedAtFromError())の JST の日付で振り分ける。★種類(error_type)では絞らない★(予定のある清掃と同じ)
施設・清掃エリア 予定の行の値 清掃結果の facility_id / section_id
削除済みの施設・清掃エリア 除く 同じく除く

2. 画面ごとの変更

2-1. 清掃詳細ログ(GET /clean-logs・CSV・/clean-logs/{id}/advice・/areas)

  • 予定外タスクを1件1行で出す。並び順・期間の絞り込みは scheduled_at(予定外は started_at)で今と同じ規則
  • CleanLogItem に追加(★YAML を先に直す★):
    • unscheduled: boolean(予定外なら true)
    • row_key: string(一意キー。予定のある行 "tt-<予定id>"、予定外 "rp-<report_id>")。画面の React の key と展開状態はこれを使う
    • id: 予定のある行は今のまま(予定の id)。★予定外は null★(report_id は19桁で安全な整数を超えるため数値の id にしない)。report_id: string | null も足す
  • 予定外の行: scheduled_at = started_at、departed = true、result_pending = false、task_status = status、事前チェック2列とその分数は null
  • 画面: 「予定」の列に時刻の代わりに「予定外」(t() を通す。EN "Unscheduled")。日付は started_at の日付
  • ★事前チェックの列は残す★(ユーザー回答 2026-10-29「1で進めてください」)。予定のある行は今のまま、予定外の行は「-」(null)。 ユーザーの「事前チェックは除いて/出す必要ない」は ★「事前チェックを別の1件として数えない」★ の意味だった(行の中の列の話ではない)。 監督は一度「列そのものを外す」と読み違えて担当Aに伝え、同日取り消した(経緯の記録)
  • アドバイス・清掃エリア別(/{id}/advice・/{id}/areas)は予定の id が要るので ★予定外の行では出さない★(▼ を出さない。判定列の位置合わせ(8d4a72e)は見えない ▼ で保つ)
  • 連続日数・L1〜L3 は予定外の行も施設の日ごとの結果に入れて数える(2-3)

2-2. ダッシュボード

  • 清掃結果(成功・失敗・未実施・結果待ち・成功率): 予定外の行も対象。直近モードの「清掃エリアごとの直近1件」の候補にも入れる(開始=started_at、終了=ended_at。終了が null なら開始で代用)
  • 予定外は成功(◎○)か失敗(△×)のどちらか。未実施・結果待ちにはならない
  • 進み具合(A案): 対象日に始まった予定外タスクを、予定件数にも出発件数にも1件ずつ足す
  • problem_logs も予定外の △× を含む

2-3. 連続日数・要レスキュー・連続問題(レポート)

  • 予定外の行も、施設のその日の結果に入れる。問題日 = その日の行のどれかが「完了率 70 未満か未実施」(今と同じ OR)
  • 予定外だけがある日も「予定がある日」として数える(連続を作る日になる)
  • 要レスキューの「未実施3日連続」には影響しない(予定外は未実施にならない)。完了率の急落の日の平均には予定外も入る

2-4. レポート(今の期間。確定期間はサマリのまま)

A案。aggregateCleanStatsByFacility() などで、予定外タスクを次のように足す:

指標 予定外タスク1件の寄与
予定回数(planned) +1
開始できた回数(executed) +1
正常終了(completed、status = 4) status = 4 なら +1
清掃完了率 1回ごとの完了率を合計と件数に入れる
エラー発生回数 予定外のエラー(1 の定義)を期間内の件数だけ足す
  • 施設別の成績・前期比も同じ集計を使う
  • エラー内訳(今の期間)にも予定外のエラーを足す。★エラー内訳の合計 = エラー発生率の分子★を保つ(aa4292d の不変条件)
  • ★確定期間は変えない★(TeamWorks のサマリ。ユーザー「サマリは変えて貰う予定」)

2-5. 清掃エリア(総清掃面積)

  • 実施回数・完了率に予定外の行を入れる(面積は今も入っている)
  • 重複の番人(1つの予定に2件以上)は予定のある行だけのまま

3. 期待値表(先に固める。監督が書いた。テストはこれをそのまま使う)

前提: 今 = 2026-10-29 10:00 JST、RESULT_WAIT_MINUTES = 120。施設 F1・清掃エリア S1。

表U1 — 1件の扱い

# 行 期待
U1-1 予定外・started_at 10-28 21:00・完了率 95.9 詳細ログに1行。unscheduled true・id null・row_key "rp-…"・departed true・判定 ◎・事前チェック null
U1-2 予定外・clean_area 0 完了率 0・判定 × ・出発 true(未実施ではない)
U1-3 clean_timetable_id = 0 と NULL どちらも予定外として同じ扱い
U1-4 予定外・task_area 0 完了率 0(今の runCompletionRate() と同じ)
U1-5 予定外・削除済みの施設 出さない・数えない
U1-6 予定のある行 今と同じ(unscheduled false・row_key "tt-")

表U2 — レポート(今の期間・A案)。施設 F1・期間 10-28 の1日

材料 内容
予定のある清掃 3回: 正常終了で完了率 80 / 正常終了で完了率 60 / 未実施
予定外 2回: status 4・完了率 100 / status 6・完了率 20
エラー 予定の id に紐づく2件 + 予定外(id 0)に紐づく1件(errored_at 10-28 22:00)+ 事前チェックの回に紐づく1件
指標 期待
予定回数 3 + 2 = 5
開始できた回数 2 + 2 = 4 → 出発成功率 80
正常終了 2 + 1 = 3 → 稼働率 60
清掃完了率 (80 + 60 + 100 + 20) ÷ 4 = 65
エラー発生回数 2 + 1 = 3(事前チェックの1件は入れない)→ エラー発生率 60
エラー内訳の合計 3(エラー発生回数と同じ)

表U3 — ダッシュボード

# 状況 期待
U3-1 清掃エリア S1 の直近: 予定 10-28 21:00(成功)より後に予定外 10-28 23:00(△)がある 直近1件は予定外 → 失敗に数える
U3-2 予定外の ended_at が今より後(清掃中) 直近の候補にしない(予定のある清掃の「終了を過ぎた」と同じ)
U3-3 進み具合 対象日 10-28: 予定7(出発6)+ 予定外2 予定 9・出発 8

表U4 — 連続日数

# 施設 F1 の日ごとの結果 期待
U4-1 10-26 予定 ×、10-27 予定外だけ(完了率 50)、10-28 予定 △ 3日連続 → L3
U4-2 10-27 に予定 ◎ と 予定外 △ その日は問題日(OR)
U4-3 10-27 予定外だけ(完了率 95) 問題日ではない → 連続が切れる

4. 分担(ファイルを重ねない)

担当 範囲
A 清掃詳細ログ: lib/db/index.ts の cleanLogs() / areaCompletions() / advice 周り、lib/db/clean-log-mapping.ts、lib/types(CleanLogItem)、d-watch-api.yaml(CleanLogItem と /clean-logs の説明)、CSV(lib/clean-logs/csv.ts)、画面(components/features/detail-log/*、lib/i18n.ts の「予定外」)、fixtures の清掃ログ
B レポート: lib/db/report-mapping.ts、lib/db/index.ts の reportKpis() / facilityPerformance() / errorBreakdown()、d-watch-api.yaml のレポートの説明、app/api/reports/__tests__
C 連続日数・要レスキュー・清掃エリア・ダッシュボード: lib/db/consecutive-source.ts、lib/db/clean-area-source.ts、lib/dashboard/*、app/api/dashboard/*、lib/judge/rescue*・lib/db/rescue-mapping.ts、連続問題(continuousProblems())
  • A の CleanLogItem の変更(unscheduled / row_key / report_id、id が null 可)は C も使う。C は A の型を前提に書いてよい(型が無ければ同じ名前で足し、A と重複したら監督がそろえる)
  • lib/db/index.ts は A・B・C が別々の関数を触る。★自分の担当の関数以外は書き換えない★。編集の前に必ず読み直す

5. 共通ルール

  • 期待値表(§3)を先にテストにする。数字を変えない
  • 予定外の判定は scheduledTimetableId() だけ。=== null / { not: null } を直書きしない
  • 事前チェックの回(precheck ≠ 0)とそれに紐づくエラーは、どの新しい集計にも入れない
  • git add -A 禁止。コミットしない(監督がまとめてコミットする)
  • 触らない: docs/decisions.md・docs/handoff/指示書AY_*・docs/handoff/設計_制御盤ログの取り出し_*・components/features/errors/ErrorContentPicker.tsx・lib/errors/error-content-label.ts とそのテスト
  • 完了したら tsc・lint(触ったファイル)・vitest 全件の件数を報告する