Newer
Older
client_dwatch / docs / handoff / 指示書S_ローカルDBで動かす第4歩_事前チェック.md

指示書S — ローカルDBで動かす第4歩(prismaSource.prechecks() を実DB経由にする)

項目 内容
実行環境 ★Cursor 内の Claude が実装します★(.claude/commands/d-watch.md の役割分担表を参照)
実行状態 ★★実行済み(2026-09-10)★★ — ★段階0〜段階5 をすべて実施。記録は docs/decisions.md の D-105 / docs/handoff.md の 追記30★
実行順 ★指示書R(D-099)の続き★。★指示書M / P / R が作った scripts/seed-local-db.ts / lib/db/prisma.ts / lib/db/local-guard.ts / lib/db/use-fixtures.ts / lib/db/ds-status.ts / lib/db/clean-log-mapping.ts / lib/format/date.ts の上に積む作業★です。★prismaSource.facilities() / schedules() / dsDevices() / dsAlerts() / cleanLogs() は1文字も変えません★。★触るコードが重なる未実行の指示書は、着手直前に自分で確認すること★(作成時点では、docs/decisions.md / docs/handoff.md を共通で触る指示書と同時に走らせないこと)
取得する決定番号 ★**着手直前に `grep -nE "^### D-(0\ 1)[0-9][0-9]" docs/decisions.md \ tail -3で最終番号を実測すること**★。★作成時点の最終はD-101(docs/decisions.md:4784)なので、最初の空き番号はD-102の見込み★。★★^### D-0だけでは3桁台(D-100 以降)を拾えない★★(D-101 冒頭の注記)。★2026-09-07 にD-068` の二重採番が実際に起きています★
前提 ★D-090 項6 / 項7 / 項8★(docs/decisions.md:3382 / :3383 / :3384。★prechecks の未決3件★) / ★D-091 項4★(:3546。precheck が分前を表す予定) / ★D-093 項2★(:3749。precheck は任意の分数。★本指示書の設計根拠★) / ★D-092★(:3599。precheck 行の仕組み) / ★D-098★(:4342。Standby.) / ★D-099★(:4442。指示書R の実行記録) / ★D-101★(:4784。認証 proxy.ts) / D-070 1-2(:1130。error_id を使い string のまま) / D-052(:325。ソート順) / D-054(:336。open の意味) / D-042(:226。書き込み4カラム) / D-050(:308) / D-056(:353) / D-077(:1847。エラー値を変換しない)
段階数 ★6段階★(段階0〜段階5)
★着手前確認★ ★★なし(すべて解決済み)★★ — ★launch_failed の判定は ★B案★(本清掃行に紐づく clean_status_histories の行が無い。cleanLogs() の departed と同じ判定を共有する)で確定★(★記録: docs/decisions.md の D-103(:5222)★)。★B案の帰結として残っていた「エラー行が無い launch_failed 行の各フィールドをどう埋めるか」も D-104 で確定★(★段階3 の「3-3b 出発失敗の行の埋め方」を見ること★)。★★新しい仕様判断は1つも要らない。ユーザーの回答を待たずに段階0 から通しで実行してよい★★
着手前の基準 ★着手直前に自分で npm test / npx tsc --noEmit / npm run lint を実行し、その実測値を基準にすること★(D-099 の完了値は「34ファイル / 658テスト / tsc 0 / lint 0」だが、★鵜呑みにしない★)
作成 2026-09-10(作成時点の決定事項最終番号 D-101 / docs/handoff.md の最新は 追記29(docs/handoff.md:2342))

実行後はこの表と、文書末尾の「実行状態」表を更新してください。

★この指示書を読んでいるあなたへ★ — .claude/commands/d-watch.md の「実装は Cursor 側」は 2つの環境の役割分担です。Cursor の中で動いている Claude は実装担当です。 ★調査や改訂ではなく実装を行ってください。★ ★★うまくいかなければ途中で止めて構いません★★ — ただし ★本指示書は API契約と型を変えます★。 ★段階1 まで進めて止めた場合、USE_FIXTURES を戻すだけでは元に戻りません★。 ★止めるなら段階の切れ目(コミット単位)で止めること★。


★★冒頭に読むべきこと — なぜ prechecks 1本だけなのか★★

★本指示書の範囲は prismaSource.prechecks() の1本だけです。★ ★★サマリ系4本(reportKpis / facilityPerformance / errorBreakdown / continuousProblems)には 手を付けません。★★

★D-090 項2(docs/decisions.md:3349 付近の分割表)の当初計画では、第4歩を 「prechecks + サマリ3本」の4本立てにしていました。★サマリ3本を外します★。理由は3つ:

# 理由
★1★ ★★prechecks は API契約(d-watch-api.yaml)と型(lib/types/index.ts)の変更を伴う★★ — ★他の7メソッドが「既存の契約に合わせて DB から値を作る」作業なのに対し、prechecks だけは「契約そのものが実データを表現できない」ことが分かっている★(D-093 項2)。★性質がまったく違うので同じ指示書に混ぜない★
★2★ ★サマリ系は「サマリテーブルを読む+当期(進行中の週/月/年)をその場集計する」という共通構造★(CLAUDE.md §4.7。★進行中の期間はサマリテーブルに存在しない★)。★4本を1本の指示書にまとめたほうが、当期集計の置き場所(D-090 項14 / 項15)を1回で決められる★
★3★ ★continuousProblems は cleanLogs の連続日数算出に依存する★(D-090 項2 の分割方針)。★lib/db/clean-log-mapping.ts:112 の computeConsecutiveProblemDays() を流用する話になるので、サマリ系と一緒に扱うのが自然★

★→ サマリ系4本は 第5歩(別の指示書)で扱います★。


★★この指示書の核心 — severity の型を変える必要がある★★

現状(★すべて実測。2026-09-10★)

箇所 実測内容
★lib/types/index.ts:124★ `export type PrecheckSeverity = "precheck_1h" \ "precheck_30m" \ "launch_failed";— ★3値固定★(:140のseverity` フィールドで使用)
★d-watch-api.yaml:1156★ ★enum: [precheck_1h, precheck_30m, launch_failed]★(PrecheckItem は :1149 から)
★components/features/alerts/AlertsScreen.tsx:20-24★ SEVERITY_LABEL が ★:21 precheck_1h: "1h前異常" / :22 precheck_30m: "30分前異常" / :23 launch_failed: "出発失敗"★。★固定ラベル★(:156 で t(SEVERITY_LABEL[item.severity]) として使用)

★注意★: ★D-093 項2-1 は lib/types/index.ts:118 / d-watch-api.yaml:1072 と記録しているが★その後の編集で行がずれている★。★上の実測値が正。行番号は必ず自分で取り直すこと★。

なぜ変えるのか

  • ★D-093 項2(docs/decisions.md:3749。ユーザー回答)★: clean_timetables.precheck は 「0 / 60 / 30」の3値ではなく、★設定値に応じた任意の分数★。★20 なら20分前★
  • ★つまり precheck = 20 の行が来ても、いまの3値では表現できない★
  • ★★現行DDLの clean_timetables.precheck は tinyint unsigned(0〜255)なので、 値が 1/2 から 60/30/20 に変わっても DB 側の型変更は不要★★(D-094 で確認済み)。 ★★変えるのは当方の API契約と型のほう★★

★推奨する設計(D-093 項2-2 の案A)★

項目 変更後
PrecheckSeverity ★`"precheck" \ "launch_failed"` の2値★
★新フィールド★ ★`precheck_minutes: number \ null`★ — ★「何分前か」を数値で持つ★
★launch_failed のとき★ ★precheck_minutes は null★
画面 ★${n}分前異常 と組み立てる★(固定ラベルをやめる)

★案B(precheck_20 のような enum を増やす)★ / ★案C(60/30 以外をその他に丸める)★は ★D-093 項2-2 で「採らない」と結論済み★。★蒸し返さないこと★。

★i18n の注意★

  • ★lib/i18n.ts はキーが日本語原文の完全一致なので、数値が混ざる文言はそのままキーにできない★
  • ★★既にある「方式A(プレースホルダ + replace)」に従うこと★★ — ★lib/i18n.ts:86-87★ が方式Aを ★推奨★ として説明している (t("他{n}件を表示 →").replace("{n}", String(n))。実例は ★:96★)。 ★方式B(前後を分割して連結)は :92-95 で「1文字キーは他画面の文言と衝突しやすい」として非推奨★
  • ★同種の前例がすぐ隣にある★: ★lib/i18n.ts:158 "第{n}回チェック": "Check #{n}" / :159 "{n}分前": "{n} min before"★。★"{n}分前異常" を足すのが自然★
  • ★★"1h前異常" / "30分前異常" / "出発失敗" は現在 lib/i18n.ts にキーが無い★★ (実測。grep でヒット0件。D-093 項2-3 が指摘済み)。 ★AlertsScreen.tsx:156 が t() を通しているのに対訳が無く、★EN でも日本語のまま出る★★。 ★新しい文言を作るついでに対訳も入れること★
  • ★★同じ画面の "残量"(AlertsScreen.tsx:167)/ "検知"(:171)にもキーが無い★★(実測)。 ★ついでに入れてよい(1行ずつなので巻き込み事故のリスクは小さい)★

★★着手前確認 — ★★なし(すべて解決済み)★★★★

★以下は解決済みの記録です。★★確認を待つ必要はありません★★(★判定=D-103 / 各フィールドの埋め方=D-104★)。

★launch_failed(出発失敗)を DB のどの状態で判定するか★ → ★★B案で確定★★

★★ユーザーの問い(2026-09-10。原文)★★:

Sは本清掃の清掃結果を抽出するところと同じではだめですか

★★この指摘が正しい★★。★launch_failed は「出発しなかった」という事象そのもの★であり、 ★指示書R(D-099)が cleanLogs() で既に判定している departed === false と同じ出来事★です。 ★清掃詳細ログ(F-02)とアラート(F-03)が、同じ出来事を別の切り口で見ているだけ★。 ★別ロジックを立てると、片方だけ直したときに数字が食い違う★。

★確定内容★:

  • ★launch_failed は ★★「本清掃行(precheck === CLEAN_TIMETABLE_PRECHECK.main)に紐づく clean_status_histories の行が無い」★★で判定する★
  • ★★cleanLogs() の departed と同じ判定を共有すること★★

根拠(★2026-09-10 実測。着手時に自分でも開いて確認すること★):

  • lib/db/index.ts:274-279 のコメント(★原文★):

    // ★「出発したか」は DB の値で決める。「予定+猶予30分」を当方で計算しない★
    //   (D-091 項1 / 指示書R 禁止事項3。LAUNCH_GRACE_MINUTES はここから読まない)
    //   ★本清掃に紐づく clean_status_histories の行が無い=未出発(未実施)★という設計。
    //   `clean_status`(-1/0/1/2)に「出発しなかった」を表す値が無く、
    //   行が作られないことが未実施を意味する、という D-091 項1 後段の読みを採った。
    //   ★実データでどのカラムが未実施を表すかは要確認★(決定台帳に記載)。
  • 実装は lib/db/index.ts:292 の ★departed: history !== undefined★ (history は :280 の historyByTimetableId.get(key(row.id)))

  • ★D-090 項6(docs/decisions.md:3382)の「★launch_failed の DB 表現も未定義★」は、 本回答で解決★(★記録は docs/decisions.md の D-103★)

選択肢(★当初の提示をそのまま残す。採用案に ✅★):

案 判定方法 帰結 採否
A ★robot_error_reports.clean_timetable_id が本清掃行(precheck = 本清掃コード)を指すエラー = launch_failed★。★precheck 行を指すものは severity: "precheck" + precheck_minutes = その行の precheck 値★ ★1つのクエリで両方を出し分けられ、severity の判定規則が「紐づく clean_timetables 行の precheck 値だけ」で閉じる★。★resolution_status / responded_at / resolved_at / note(=書き込み許可4カラム。D-042)を launch_failed にも持たせられる★。★ただしシードに本清掃行のエラーを足す必要がある(段階1)★ ★不採用★ — ★「エラー行があること」を出発失敗の条件にすると、cleanLogs() の departed と別の定義になる★。★同じ出来事に2つの判定ができ、片方だけ直したときに /detail-log と /alerts で数字が食い違う★
★B ✅★ ★本清掃行に clean_status_histories が無い(=未出発。D-099 の departed 判定)ことを launch_failed とする★ ★cleanLogs() の departed=false と定義が完全に一致する★。★しかしエラー行が存在しないので、robot_status / detected_at / resolution_status / responded_at / resolved_at / note の置き場が無い★ ★★採用★★ — ★同じ出来事は同じ判定で出す★。★下記「残る課題」で4カラムの扱いを決めること★
C ★第4歩では launch_failed を出さない(precheck 行由来の severity: "precheck" のみ返す)★ ★実装は最小。TeamWorks の回答を待てる★。★ただし /alerts から「出発失敗」が消え、fixtures との突き合わせ(段階4)で常に差が出る★ ★不採用★ — ★待つ必要が無かった★。★判定は既にコードの中にあり、TeamWorks の回答を待つ論点ではない★

★★実装上の注意 — 判定を2箇所に書かないこと★★:

  • ★現状、この判定は lib/db/clean-log-mapping.ts に切り出されていない★ — ★lib/db/index.ts:292 にインラインで書かれている★ (★同ファイルの export は formatModelName / robotStatusFromErrors / isProblemCompletion / computeConsecutiveProblemDays の4つのみ。2026-09-10 実測★)
  • ★★したがって「流用」ではなく「切り出し」になる★★ — ★lib/db/clean-log-mapping.ts に 純粋関数として出し、cleanLogs() と prechecks() の両方から呼ぶこと★。 ★cleanLogs() の挙動は1ビットも変えない(禁止事項の「既存5メソッドは1文字も変えない」は、 この切り出しに伴う呼び出し置換のみ例外とし、段階5 で記録すること)★

★B案の帰結(★D-104 で確定済み。段階3 で「決める」必要はもう無い★)★:

  • ★B案では launch_failed に対応する robot_error_reports の行が存在しない★ため、 ★resolution_status / responded_at / resolved_at / note(D-042 の書き込み許可4カラム)の 置き場が無い★
  • ★★埋め方は D-104 で確定した★★ — ★resolution_status は unresolved 固定 / note / responded_at / resolved_at は null★。★詳細と根拠は段階3 の 「3-3b 出発失敗の行の埋め方」を見ること★。★precheck 由来の行だけが4カラムの実体を持つ、という形になる★

★★すべて解決済みなので、そのまま段階1 に入ってよい★★。 ★決定は docs/decisions.md の D-103(判定)と D-104(埋め方)に記録済み★。


★調査済みの事実(★現物で裏を取ってから使うこと★)★

1. ★PrecheckItem の各フィールドの取得元と、未決3件の扱い★

★PrecheckItem は lib/types/index.ts:138-155★。

フィールド 取得元 状態・扱い
★id★ ★robot_error_reports.id / error_id(★どちらも varchar(50)★。prisma/schema.prisma:303-304)★ ★D-090 項7(docs/decisions.md:3383): 型が number(lib/types/index.ts:139)と食い違う★。★D-070 1-2(:1130)で「error_id を使い string のまま」と方針は確定済み、★実装が未着手★★ → ★★段階1 で string にする★★
★severity★ clean_timetables.precheck ★D-090 項6: 未決。★上記の型変更(2値化 + precheck_minutes)で解決する★
★precheck_minutes(新設)★ clean_timetables.precheck の値そのもの ★launch_failed のときは null★
serial_number robot_error_reports.serial_number ★varchar(20)。NOT NULL★
facility_id / facility_name robot_error_reports.facility_id → ★facility リレーションを include★ ★facility_id は FK があるのでリレーションで辿れる(prisma/schema.prisma:329)★。★BigInt → Number()★
scheduled_at ★clean_timetables.start_at(本清掃行)★ ★toJstIso()(lib/format/date.ts)を使う★(指示書P で導入済み)。★precheck 行の start_at ではなく親の本清掃行の値★(parent_id で引く)
★robot_status★ ★robot_error_reports.error_type の生値★ ★D-077 / D-098。日本語に変換しない★。★エラーが無ければ ROBOT_STATUS_STANDBY(lib/config/index.ts:72 = "Standby.")★。★lib/db/clean-log-mapping.ts:54 の robotStatusFromErrors() がこの規則を既に実装している → 流用すること★
★detected_at★ ★robot_error_reports.errored_at(prisma/schema.prisma:324。★nullable★)または created_at(:327。★NOT NULL / default now()★)★ ★★どちらを使うか未決。決めて記録すること★★。★推奨: errored_at ?? created_at(errored_at を優先し、null のときだけ created_at に落とす)★。★理由: errored_at が「エラーが起きた時刻」で意味的に正しいが nullable なので、NOT NULL の created_at を保険にする★
resolution_status robot_error_reports.status(:322。★Int @default(0) @db.UnsignedTinyInt★) ★0 = unresolved / 1 = in_progress / 2 = resolved★。★lib/db/ds-status.ts の resolutionStatusFromControlErrorStatus() と同じ値域なので、同じ作法で純粋関数に切り出すこと★
responded_at / resolved_at 同名カラム(:325 / :326。★どちらも nullable★) ★書き込み許可4カラム(D-042。docs/decisions.md:226)★。★本作業は読むだけ★
note robot_error_reports.comment(:323。★nullable★) 同上
★battery_level★ ★★取得元が無い★★ ★D-090 項8(docs/decisions.md:3384): robots.battery は現在値のみで履歴が無い★。★★null を返すことを推奨★★(「事前チェック時点の残量」は DB から取れないため、当方で現在値を代入すると別時点の値を過去のアラートに貼ることになる)。★決めて記録すること★

2. ★API契約(YAML)の側にも抜けがある(実測)★

★d-watch-api.yaml:1149-1176 の PrecheckItem には★:

  • ★facility_id が無い★(lib/types/index.ts:142 にはある)
  • ★battery_level が無い★(lib/types/index.ts:154 にはある。AlertsScreen.tsx:165-169 が表示している)

★★段階1 でこの2つも足すこと★★(★CLAUDE.md §6-1「API契約を変えたくなったら先に YAML を直す」★)。

3. ★★id を string にすると影響が出る箇所(★洗い出し済み。自分で grep し直すこと★)★★

# 箇所 内容
1 ★lib/types/index.ts:139★ id: number → ★id: string★
2 ★d-watch-api.yaml:1153★ 付近 id: { type: integer, example: 1 } → ★{ type: string }★
★3★ ★d-watch-api.yaml:442★ ★/prechecks/{id}/status が共有パラメータ PathId(:997-1001。★type: integer★)を参照している★。★★PathId は他に6箇所(:337 :370 :769 :957 :969)から参照されているので、PathId 自体を変えないこと★★ → ★/prechecks/{id}/status にだけインラインの string パラメータを書く★
★4★ ★lib/prechecks/query.ts:132★ sortPrechecks() の同値時タイブレークが ★a.id - b.id(数値減算)★。★string では動かない★ → ★a.id < b.id ? -1 : a.id > b.id ? 1 : 0 等に変える★。★D-052(docs/decisions.md:325)の「detected_at 降順 / 同値は id 昇順」という契約は変えないこと★
5 ★lib/fixtures/prechecks.ts:21 :36 :52 :69 :84 :101 :116 :131★(8件) ★id: 1 → id: "1" 等★。★★lib/fixtures/ の改変になるので、指示書R 禁止事項12(CLAUDE.md §9.5)に抵触する — ★型変更に伴う必然の追従なので本指示書では許可する★。★値の意味は変えないこと(1 → "1" のように同じ文字列にする)★★
6 ★lib/prechecks/__tests__/query.test.ts★(:14 :143-145 :151-153 :158 :162 :166 :170 :194 :284 :293) ★id と severity の両方が変わる★。★期待値表を先に直してからテストを走らせること★(CLAUDE.md §9.4)
7 ★components/features/alerts/AlertsScreen.tsx:149★ key={item.id} — ★React の key は string でも動くので変更不要★(確認だけすること)
8 app/api/prechecks/route.ts ★id を直接触っていない(:42 で型として使うだけ)ので変更不要の見込み★。★確認すること★

★★/prechecks/{id}/status(PATCH)の API ルートはまだ実装されていません★★ (app/api/prechecks/ には route.ts しかない。実測)。★本指示書で作らないこと★。

4. ★落とし穴(★指示書R と共通。現物で確認すること★)★

# 落とし穴
★1★ ★robot_error_reports.clean_timetable_id(prisma/schema.prisma:312)に FK も KEY も無い★(★しかも BigInt? で nullable★)。★include で clean_timetables を辿れない → 別クエリ+メモリ上の手動 join★(cleanLogs() と同じ。★lib/db/index.ts:205-250 付近の実装をそのまま流用できる★)
★2★ ★robot_error_reports に deleted_at が無い★(prisma/schema.prisma:299-337 を実測)→ ★論理削除の絞り込みを書かない★。★書くと型エラーになる(=安全側)。無理に回避しないこと★
★3★ ★id / error_id は varchar(50)★(:303-304)→ ★Number() は NaN になっても例外を出さない★。★★本作業では Number() を通さない(string のまま返す)のが正解★★
★4★ ★BigInt → Number()★ を忘れると JSON.stringify が例外を投げる(NextResponse.json が 500 になる。★tsc は捕まえない★)。★facility_id が該当★
★5★ ★RobotErrorReport には @id が無く error_id @unique だけ★(:303-304)→ ★Prisma の findUnique は error_id でしか引けない★
★6★ ★clean_timetables.start_at は datetime(TZ情報なし)★。★API は +09:00 付き ISO8601 → toJstIso() を使う★

5. ★シードの状況(★実測して確認すること★)★

★指示書R(D-099 段階1)で clean_timetables / clean_statuses / clean_status_histories / robot_error_reports はシード済み★(scripts/seed-local-db.ts。★npm run seed:local★)。 ★ただし prechecks の観点では足りないものがあります★:

# 不足 帰結 対応
★1★ ★robot_error_reports の status / comment / responded_at / resolved_at を1件も入れていない★(scripts/seed-local-db.ts:689-705 を実測) ★status は default 0 なので、★全件が unresolved・note が null★になる★。★/alerts の「対応中 / 解消」が1件も出ない★ ★段階1 に含めること★。★lib/fixtures/prechecks.ts の8件と同じ分布(未解消3 / 対応中2 / 解消3)になるよう、シードで値を散らすこと★
★2★ ★本清掃行に紐づくエラー行が無い★(precheck 行にしか作っていない) ★★B案(確定)ではこれが正常★★。★launch_failed はエラー行ではなく、本清掃行に clean_status_histories の行が無いことで判定する(cleanLogs() の departed と同じ。D-103)ため、★★エラー行が無いことがそのまま launch_failed の存在を意味する★★(エラー行の有無を条件にする必要が無い) ★不採用の A案ならここでエラー行の追加が要ったが、B案ではこの不足を埋める作業は不要★。★段階1 に追加のシード作業は無い★
3 ★シードのエラー行は lib/fixtures/clean-logs.ts 由来であり、lib/fixtures/prechecks.ts の8件とは別物★ ★段階4 の USE_FIXTURES 突き合わせで件数が一致しない★ ★★これは想定内★★。★「同じ8件を再現する」ことは目指さない★。★確認するのは変換規則が正しいこと(severity / precheck_minutes / robot_status / resolution_status の作られ方)★

★シードを触る場合の作法★: ★deleteMany の逆順(scripts/seed-local-db.ts:392-395)を崩さない★ / ★2回続けて実行しても結果が同じであること★ / ★実在の住所・電話番号・社名・人名を入れないこと★ (CLAUDE.md §7。★既存のダミー値は :229-231 の ERROR_LEVEL_PLACEHOLDER / ERROR_DETAIL_PLACEHOLDER★)。


★禁止事項(この指示書の全段階に効きます)★

# ★してはいけないこと★ 理由
★1★ ★★サマリ系4本(reportKpis / facilityPerformance / errorBreakdown / continuousProblems)に手を付けること★★ ★第5歩の範囲★。★共通構造でまとめて書くほうが良いと本書冒頭で結論済み★
★2★ ★★エラー種別を日本語に変換すること / 7分類に寄せること★★ ★D-077(docs/decisions.md:1847)/ D-088 / D-098★。★error_type の値をそのまま robot_status に入れる★
★3★ ★★precheck の値(0/1/2、60、30 など)をコードに直書きすること★★ ★CLAUDE.md §7 / D-091 項4 / D-093 項2★。★lib/config/index.ts:49-54 の CLEAN_TIMETABLE_PRECHECK を使う★。★値が変わったとき1箇所で追従できる形を壊さない★
★4★ ★deleted_at が無いテーブルで論理削除の絞り込みを書くこと★ ★型エラーになる(=安全側)。落とし穴2★
★5★ ★clean_timetables.inactive で絞ること★ ★第1〜3歩と同じ方針(lib/db/index.ts:180)。★仕様が未確定なので絞らない側に倒す★
★6★ ★★メインDB(TeamWorks 側)に接続・書き込みすること / dwatch_ddl に書き込むこと★★ CLAUDE.md §2 / §7 / D-042。★作業は dwatch_local の中だけ★。★接続先ホスト名は未受領★
★7★ ★prisma migrate / db push / db pull を使うこと / prisma/schema.prisma を手で書き換えること★ ★CLAUDE.md §7 / D-056 / D-059★。★prisma/schema.prisma:301-302 のコメントが D-070 1-2 より古い(「API 契約を変更しない」と書いてある)が、★それでも手で直さない★★
★8★ ★prismaSource の既存5メソッド(facilities / schedules / dsDevices / dsAlerts / cleanLogs)を変更すること★ ★指示書M / P / R の成果物★。★本作業は「その隣に1本足す」だけ★
★9★ ★lib/db/prisma.ts / local-guard.ts / use-fixtures.ts / ds-status.ts を変更すること★ ★完成しており、そのまま効く★。★特に use-fixtures.ts は Edge Runtime バンドル破壊の修正そのもの★(D-086)
★10★ ★lib/db/ にフィルタ・ページング・ソートを持ち込むこと★ ★lib/db/index.ts:71-75 の設計方針★。★prechecks には judge も連続日数も無いので、この例外は使えない★
★11★ ★書き込み許可4カラム以外に書き込むこと / 本作業で DB に書き込むこと★ ★D-042★。★本作業は読むだけ(シードを除く)★
★12★ ★テストを DB 依存にすること★ ★遅く・不安定になり、DB が空の環境で落ちる★。★テストは fixtures と純粋関数だけ★
★13★ ★git add -A を使うこと★ CLAUDE.md §7。★必ずファイルパスを明示★
★14★ ★.env.local をコミットすること / DB の認証情報を文書やコードに書くこと★ CLAUDE.md §9.3。★確認後は USE_FIXTURES=true に戻す★
★15★ ★npm run dev を Bash で起動すること★ ★Browser pane の preview_start を使う★(段階4)

作業の順序(★崩さないこと★)

★段階0(前提の確認。★読むだけ・コミットなし★)→ 段階1(API契約と型 + 必要ならシードの補完) → 段階2(fixtures と画面)→ 段階3(prismaSource.prechecks())→ 段階4(動作確認。★コミットなし★) → 段階5(記録と push)★。★★段階1・2・3・5 でそれぞれ1コミット★★。

★段階ごとに npx tsc --noEmit / npm run lint / npm test を実行し、通ってからコミットすること★ (D-050。docs/decisions.md:308)。


段階0 — 前提の確認(★読むだけ・コミットなし★)

# 対象 見る点
1 ★D-090 項6 / 項7 / 項8★(docs/decisions.md:3382-3384) ★未決3件の原文★
2 ★D-093 項2★(:3749。特に ★2-2 の案A(:3774)★ と ★2-3 の注意(:3782)★) ★本指示書の設計根拠★
3 ★D-091 項4★(:3546)/ ★D-070 1-2★(:1130)/ ★D-098★(:4342) ★precheck が分前を表す予定★ / ★error_id を string のまま★ / ★Standby.★
4 ★★prismaSource.cleanLogs()(lib/db/index.ts:186-320)★★ ★これが雛形★。★手動 join(:205-250 付近)/ 遅延 import / Number() / toJstIso() / docstring の書き方をそのまま流用する★
5 ★lib/db/clean-log-mapping.ts(全141行)★ ★robotStatusFromErrors()(:54)は prechecks でもそのまま使える★。★RobotErrorLike(:32)も同じ★
6 ★lib/db/ds-status.ts★ ★resolutionStatusFromControlErrorStatus() の作法(0/1/2 → 3値)を robot_error_reports.status に流用する★
7 ★scripts/seed-local-db.ts(全784行。特に :646-712)★ ★precheck 行とエラー行の作り方★。★「調査済みの事実」5 の不足2件を自分で確認すること★
8 ★lib/i18n.ts:80-97 / :155-160★ ★方式A の説明と既存の実例★

接続先の確認とこの段階の完了条件

  • ★.env.local の DATABASE_URL が dwatch_local を指していることを確認する★。 ★値そのものを画面・ログ・文書に出さないこと★(CLAUDE.md §9.3)
  • ★.env.local に ALLOW_DIRECT_ACCESS=true があることを確認する★(★作成時点では設定済み★。 D-101 項2。★無いと proxy.ts が全ルートを止めるので段階4 で詰まる★)
  • ★着手前の基準値(npm test のファイル数・テスト数 / tsc / lint)を自分で実測して控えた★
  • ★★着手前確認は解決済み(launch_failed の DB 表現 = B案。D-103)。新たに回答を得る必要はない★★

段階1 — ★API契約と型を変える★

★実装より先に契約を直す★(CLAUDE.md §6-1)。

1-1. d-watch-api.yaml

  • ★:1156 の enum を ★[precheck, launch_failed]★ に変更し、description も直す★
  • ★precheck_minutes を追加★({ type: integer, nullable: true }。 ★description に「何分前のチェックか。launch_failed のときは null」と書く★)
  • ★:1152 付近の id を { type: string, example: "RER-1001-1" } に変更★
  • ★facility_id と battery_level を追加★(「調査済みの事実」2)
  • ★:442 の PathId 参照を、/prechecks/{id}/status 専用のインライン string パラメータに置き換える★ (★★components/parameters/PathId 自体は変えない★★。他6箇所が参照している)

1-2. lib/types/index.ts

  • ★:124 PrecheckSeverity を "precheck" | "launch_failed" に★
  • ★:139 id: number → id: string★(D-090 項7 / D-070 1-2。★決定済みなのでここで実装する★)
  • ★PrecheckItem に precheck_minutes: number | null を追加★(★コメントで「launch_failed は null」 「値は clean_timetables.precheck の生値」を明記★)

1-3. ★id を string にした影響の追従★

★「調査済みの事実」3 の表の #4・#6 を直す★(lib/prechecks/query.ts:132 のタイブレークと、 lib/prechecks/__tests__/query.test.ts)。★#5(fixtures)は段階2 でまとめて直してよい★。

1-4. ★シードの補完(「調査済みの事実」5)★

  • ★robot_error_reports に status / comment / responded_at / resolved_at を入れる★ (★分布は fixtures の8件に倣う。値は決め打ちでよいが、規則をコメントに書くこと★)
  • ★★B案確定のため不要★★: ★不採用の A案なら本清掃行に紐づくエラー行を数件作る必要があったが、 B案の launch_failed はエラー行を使わず「本清掃行に clean_status_histories が無いこと」 (departed。D-103)で判定するため、ここでのエラー行追加はしない★
  • ★冪等であること(2回実行して同じ結果)★ / ★件数を出力すること★

この段階の完了条件

  • ★npx tsc --noEmit エラー0 / npm run lint 0 / npm test 全パス★
  • ★d-watch-api.yaml と lib/types/index.ts の内容が一致していること★(★enum / nullable / 型★)
  • ★components/parameters/PathId に差分が無いこと★
  • ★シードを触った場合、2回実行して結果が同じであること★
  • ★ここで1コミット★

段階2 — fixtures と画面を追従させる

2-1. lib/fixtures/prechecks.ts

  • ★8件すべてを新しい型に★: ★id を string に★ / ★severity: "precheck_1h" → severity: "precheck", precheck_minutes: 60★ / ★"precheck_30m" → severity: "precheck", precheck_minutes: 30★ / ★"launch_failed" → precheck_minutes: null★
  • ★分数は直書きせず PRECHECK_MINUTES_BEFORE(lib/config/index.ts:34)から取ること★(禁止事項3)
  • ★★境界値を1件足すこと★★ — ★precheck_minutes: 20(設定値以外の任意の分数)の行★。 ★D-093 項2 の「20 なら20分前」を画面と型が本当に表現できるかの検証になる★。 ★★これが本指示書で fixtures を触る主目的なので、忘れないこと★★

2-2. components/features/alerts/AlertsScreen.tsx

  • ★:20-24 の SEVERITY_LABEL を分数から組み立てる形に変える★:
    • ★launch_failed → t("出発失敗")★
    • ★precheck → t("{n}分前異常").replace("{n}", String(item.precheck_minutes))★
    • ★★precheck_minutes が null の precheck(起きないはずの組み合わせ)をどう出すか決めること★★ (★推奨: t("事前チェック異常") にフォールバックし、黙って空文字を出さない★)
  • ★:26-28 の severityColor() は launch_failed かどうかで分岐しているだけなのでそのまま動く★ (確認だけすること)

2-3. ★i18n の対訳を入れる★

★lib/i18n.ts に追加する(★方式A。:86-87 / :158-159 の前例に倣う★)★:

キー EN 備考
★"{n}分前異常"★ ★"{n} min before: error" 等★ ★方式A。:159 の "{n}分前" の隣に置くと読みやすい★
★"出発失敗"★ "Launch failed" ★★現在キーが無い(EN でも日本語のまま出ている)★★
★"事前チェック異常"★ "Pre-check error" ★2-2 のフォールバックを作る場合★
★"残量" / "検知"★ "Battery" / "Detected" ★同じ画面でキーが無いもの(AlertsScreen.tsx:167 / :171)★

★★"1h前異常" / "30分前異常" はキーごと消える★★(★組み立て式に変わるため★)。 ★キーが無かったので削除対象も無い(実測)★。

2-4. ★判断事項 — robot_status を生値に寄せるか★

★lib/fixtures/prechecks.ts の robot_status は現在 ★日本語6値★★ ("ハンドルアップ" / "オフライン" / "充電中" / "セキュリティ有効" / "緊急停止")。

  • ★★指示書K(D-088。docs/decisions.md:3084)で error_type を英語識別子に寄せたのと 同じ扱いにするのが一貫する★★。★DB 経由では error_type の生値がそのまま入るため★
  • ★寄せると lib/theme-status.ts の色分けがグレーになる★(★日本語6文字列の完全一致で引くため。 lib/config/index.ts:197 の ROBOT_STATUS_TONES)。★これは D-077 の想定挙動★
  • ★指示書R では「エラー無し時の値」だけを Standby. にし、 ★ROBOT_STATUS_TONES には足さなかった★★(D-099 項2-2。lib/config/index.ts:185-187 のコメント)
  • ★★判断してから実装し、理由を記録すること★★。★寄せない場合、段階4 の突き合わせで robot_status に差が出るのは想定内として記録する★

この段階の完了条件

  • npx tsc --noEmit エラー0 / npm run lint 0 / npm test 全パス
  • ★USE_FIXTURES=true のまま /alerts が表示され、★「60分前異常」「30分前異常」「20分前異常」 「出発失敗」がすべて正しく出ること★★
  • ★EN 表示に切り替えて、追加した対訳が効いていること★
  • ★★0/1/2 や 60 / 30 が AlertsScreen.tsx / lib/fixtures/ に直書きされていないこと★★
  • ★ここで1コミット★

段階3 — prismaSource.prechecks() を実装する

3-1. 作るもの

★lib/db/index.ts:321-323 の notImplemented("prechecks") を実装に置き換える★。 ★cleanLogs()(:186-320)と同じ形にすること★(遅延 import → クエリ → 手動 join → map)。

3-2. ★手動 join(落とし穴1)★

  1. ★robot_error_reports を findMany(★facility は FK があるので include してよい★)★
  2. ★clean_timetables を findMany(★clean_timetable_id に FK が無いので include で辿れない★)★
  3. ★clean_timetables.id をキーに Map を作る★
  4. ★エラー行 → 紐づく clean_timetables 行 → precheck の値 で severity / precheck_minutes を決める★
  5. ★scheduled_at は親の本清掃行の start_at★ — ★precheck 行なら parent_id で親を引く / 本清掃行なら自分自身★
  6. ★clean_timetable_id が null のエラー行★(prisma/schema.prisma:312 は nullable)★の扱いを 決めて記録すること★(★推奨: prechecks の対象外として除外する。 「清掃タイムテーブルに紐づかないエラー」は事前チェックのアラートではないため★)

★★この方針(なぜ include が使えないのか)をコードコメントに残すこと★★ — ★D-094 で TeamWorks に FK 追加を再依頼中なので、FK が付いたら include に書き換えられる★。 ★根拠(prisma/schema.prisma:312 に FK も KEY も無い)を書くこと★。

3-3. ★severity の組み立て★

★★precheck = 0(本清掃)の行に紐づくエラーは launch_failed に相当するか?★★ — ★B案で確定(D-103)★。★D-090 項6 が「launch_failed の DB 表現は未定義」としていたが、これで解決済み★。

紐づく clean_timetables 行 severity precheck_minutes
★precheck === CLEAN_TIMETABLE_PRECHECK.main(本清掃)★ ★A案なら launch_failed★ ★null★
★それ以外(事前チェック行)★ ★"precheck"★ ★★precheck の値をそのまま★★ …ただし ★現行DDLは 1 / 2 のコード値★(D-091 項4 の切替前)なので、★CLEAN_TIMETABLE_PRECHECK.rounds と PRECHECK_MINUTES_BEFORE の対応表で分数に直す★(lib/config/index.ts:49-54 のコメントがこの対応を明記している)。★★切替後は「そのまま使う」に変わる — その分岐を1箇所の純粋関数に閉じ込め、コメントで明示すること★★
  • ★★0/1/2 や 60/30 を直書きしないこと★★(禁止事項3)
  • ★★決めたことを記録すること★★(段階5)

3-3b. ★★出発失敗の行の埋め方(D-104 で確定。判断不要)★★

★上の 3-3 の表は「A案なら launch_failed」という条件付きで書かれているが、 ★★採ったのは B案 なので、その分岐には入らない★★★ — ★launch_failed はエラー行から作らない(本清掃行に紐づく clean_status_histories が 無いことから作る。D-103)★。★したがって「本清掃行を指すエラー行」を launch_failed に 読み替えてはいけない★。

★★出発失敗の行には robot_error_reports の行が存在しない★★ため、エラー行から取るはずの フィールドが空になる。★埋め方はすべて既存の前例に揃える。新しい判断は要らない★:

フィールド 確定内容 なぜそうするか 前例
★id★ ★本清掃行の clean_timetables.id★(★BigInt なので Number() を通す★) ★1回の清掃を一意に指す最小の単位★。★エラー行が無いので error_id は使えないが、出発失敗は「その本清掃が出発しなかった」という事象なので、本清掃行の id で一意に決まる★ ★D-099 項4(docs/decisions.md:4559)★ — ★CleanLogItem.id にまったく同じ値を採っている★(★原文: 「id(D-090 項3 の解決): 本清掃行(precheck = 0)の clean_timetables.id。1回の清掃を一意に指す最小の単位。事前チェック行は parent_id でこれを指すので、1件の CleanLogItem に対応する id が必ず1つに決まる」★)。★実装は lib/db/index.ts:283 の id: Number(row.id)(row は本清掃行)★
★robot_status★ ★Standby.★ ★エラー行が0件のときの値★。★自分で文字列を書かず、既存の純粋関数に空配列を渡すだけでよい★ ★D-098★。★robotStatusFromErrors()(lib/db/clean-log-mapping.ts:54)が errors.length === 0 のとき ROBOT_STATUS_STANDBY(lib/config/index.ts:72 = "Standby.")を返す★。★cleanLogs() も lib/db/index.ts:271 で同じ関数を呼んでいる★
★resolution_status★ ★unresolved 固定★ ★「まだ誰も対応していない」の意味★。★PrecheckStatus は unresolved / in_progress / resolved の3値(lib/types/index.ts:125。YAML は d-watch-api.yaml:1165)なので、状態を持てない行は先頭の値に倒すしかない★ ★precheck 由来の行が robot_error_reports.status の default 0(= unresolved)から作られるのと同じ値★
★note / responded_at / resolved_at★ ★すべて null★ ★エラー行が無いので備考も日時も存在しない★。★null は「値が無い」という事実そのもの★ ★型定義がいずれも nullable(lib/types/index.ts:149 / :151 / :152)★

★★「その他」「不明」のような語を使わないこと★★ — ★D-089 項3(docs/decisions.md:3249-3251)で 同じ判断をしている★: ★「『その他』は D-067(破棄済み)の『未知の値をその他に寄せる』方針と 紛らわしい」★。★やり方(1箇所の定数に固定値を置く)は同じでよいが、★語だけは避ける★★。

★★id を D-099 と同じにする利点★★:

  • ★★清掃詳細ログ(F-02)とアラート(F-03)に、同じ清掃について同じ番号が出る★★
  • ★同じ出来事を別の切り口で見ているだけなので、★将来2画面を突き合わせるときに揃う★★ (★D-103 項1 で「同じ出来事は同じ判定で出す」と決めたのと同じ理屈を、判定だけでなく識別子にも適用する★)
  • ★★注意★★: ★precheck 由来の行は error_id(string)、launch_failed の行は本清掃行の id(数値の文字列化)になる★。 ★PrecheckItem.id は段階1 で string にするので型は揃う★。★★衝突しないことを確認し、 必要なら混ざらないことが分かる形にするかを判断して段階5 に記録すること★★

★★申し送り(第4歩のスコープ外。将来の課題)★★

  • ★出発失敗の行には robot_error_reports の行が無いため、★★将来「対応状況の更新機能」を 作るときに書き込み先が無い★★★
  • ★★いまは問題にならない★★ — ★PATCH /prechecks/{id}/status は API契約にあるだけで未実装★ (app/api/prechecks/ には route.ts しかない。★実測して確認すること★)。 ★画面も表示だけで、components/features/alerts/AlertsScreen.tsx の onClick は フィルタ切替の3つ(:117 / :124 / :131。すべて setStatusFilter)だけ★。★D-Sec 側も同様★
  • ★将来の選択肢は2つ★:

    | 案 | 内容 | 前例 | |---|---|---| | ★1★ | ★DFA 独自ストアに持つ★ | ★D-076(docs/decisions.md:1762)の D-Sec デバイス備考★ — ★lib/settings/ds-device-notes.ts が json-store.ts を薄く包み、GET のときに DB 由来の値へ上書きしている(app/api/ds-devices/route.ts)★ | | ★2★ | ★TeamWorks に「出発失敗のときもエラー行を作る」ことを依頼する★ | ★D-042 の書き込み許可4カラムがそのまま使えるようになる★ |

  • ★★本指示書ではどちらも実装しない★★。★段階5 で申し送りとして記録するだけでよい★

3-4. ★その他のフィールド★

  • ★robot_status★: ★robotStatusFromErrors()(lib/db/clean-log-mapping.ts:54)を流用★。 ★1件のエラー行を扱うので配列に包んで渡すだけでよい★
  • ★detected_at★: ★決めた規則(推奨 errored_at ?? created_at)で toJstIso() を通す★
  • ★resolution_status★: ★status(0/1/2)→ 3値。★純粋関数に切り出す★(ds-status.ts と同じ作法)★
  • ★battery_level★: ★null★(D-090 項8。★理由をコメントに残すこと★)
  • ★id★: ★error_id を使う★(D-070 1-2)。★Number() を通さない★
  • ★facility_id★: ★Number()★(BigInt。落とし穴4)

3-5. ★変換ロジックは純粋関数に切り出してテストを書く★

  • ★置き場所★: ★lib/db/precheck-mapping.ts を新設するか、lib/db/clean-log-mapping.ts に足すか★を 判断して記録すること(★推奨: 新設。cleanLogs 側のテストと混ざらない★)
  • ★切り出す候補★: ★severity / precheck_minutes の組み立て(precheck コード → 2値+分数)★ / ★resolution_status の変換★ / ★detected_at の選択★
  • ★★期待値表を先に作ること★★(CLAUDE.md §9.4)。★境界値: 本清掃コード / rounds の各値 / 未知の precheck 値(例 20)/ status の 0・1・2・想定外値 / errored_at が null★
  • ★DB 依存のテストは書かない★(禁止事項12)

この段階の完了条件

  • npx tsc --noEmit エラー0 / npm run lint 0 / npm test 全パス
  • ★prismaSource の既存5メソッドに差分が無いこと★(禁止事項8)
  • ★app/api/prechecks/route.ts に差分が無いこと★(★データ層の差し替えだけで動くことが正しさの証明★)
  • ★手動 join の理由・severity の組み立て規則・battery_level を null にした理由が コメントに残っていること★
  • ★ここで1コミット★

段階4 — 動作確認

4-1. 起動と★認証★

  • ★.env.local の USE_FIXTURES を false にする★ (★lib/db/use-fixtures.ts は文字列 "false" のみを見る。FALSE や 0 では切り替わらない★)
  • ★npm run dev を Bash で起動しないこと。Browser pane の preview_start を使う★ (.claude/launch.json に d-watch(npm run dev / port 3000)がある)
  • ★★認証が入ったので注意★★ — ★指示書I(D-101。docs/decisions.md:4784)で proxy.ts が入り、 ★全ルートが保護されている★★
    • ★ローカルで確認するには .env.local に ALLOW_DIRECT_ACCESS=true が必要★ (★作成時点では設定済み。自分で確認すること★)
    • ★★/login でトークンを入れる必要があるかを実際に確かめること★★ (app/login/page.tsx / app/api/auth/login が存在する)。 ★必要なら .env.local の AUTH_TOKEN を使う(★値を文書やログに出さないこと★)★
  • ★環境変数を変えたら dev サーバを再起動し、反映を確認すること★
  • ★Browser pane が黒画面 / 0×0 になる既知の問題★: ★preview_start に url を渡して開き直す★。 ★指示書R(D-099 段階3)では Browser pane が使えず curl で代替した★。 ★同じ状況なら curl でよいが、★その旨と「目視できなかった項目」を必ず記録すること★★

4-2. ★★確認1 — USE_FIXTURES の true / false で /api/prechecks を突き合わせる★★

  • ★同一クエリで両方のレスポンスを取得し、差分を1件ずつ説明できること★
  • ★★件数は一致しません★★ — ★fixtures の8件とシード由来の行は別物(「調査済みの事実」5-3)★。 ★確認するのは変換規則であって件数ではない★
  • ★確認する点★:
# 項目
1 ★severity が "precheck" / "launch_failed" の2値だけであること★(★旧3値が1件も出ないこと★)
2 ★precheck_minutes が 数値で入り、launch_failed では null であること★
3 ★id が 文字列であること★(★"RER-..." 形式。NaN や null が混ざっていないこと★)
4 ★scheduled_at / detected_at / responded_at / resolved_at が ★+09:00 付き★ であること★
5 ★robot_status に英語識別子(例 Handrail Lifted)または Standby. が出ること★。★★日本語(「ハンドルアップ」等)が出たら変換してしまっています★★
6 ★resolution_status が unresolved / in_progress / resolved に散っていること★(★全件 unresolved なら段階1-4 のシード補完が効いていない★)
7 ★battery_level が全件 null であること★(D-090 項8)
8 ★ソート順が detected_at 降順・同値は id 昇順であること★(D-052)

4-3. ★確認2 — /alerts 画面★

# 項目
1 ★/alerts が実DB経由で表示されること(HTTP 200)★
2 ★★「◯分前異常」が正しく出ること★★(★precheck_minutes の値がそのままラベルに出る★)
3 ★「出発失敗」が出ること★(★B案確定。不採用の C案を選んでいた場合のみ出ないのが正しかった★)
4 ★色分け(severityColor)が launch_failed だけ赤(c.ng)になること★
5 ★robot_status のバッジがグレーになること★(★生値は日本語6値に一致しない。想定挙動★)
6 ★サマリカード(未解消 / 解消 / 全件)の数が items と整合すること★(D-054)
7 ★EN 表示で対訳が効いていること★

4-4. ★確認3 — 回帰していないこと★

画面 期待
★/detail-log / /dashboard / /facilities / /schedules / /ds-logs / /ds-alerts★ ★200。回帰していないこと★(★特に /detail-log 253件 / /dashboard が 9 / 9。D-099★)
/reports ★500★(第5歩)
  • ★エラーメッセージが [db] xxx のPrisma実装はまだありません。… であること★(lib/db/index.ts:155-159)
  • ★★404 が出たら Edge Runtime バンドル破壊の回帰を疑うこと★★(D-086)。 ★500 = 想定どおりの未実装 / 404 = ビルドが壊れている★

4-5. ★ログの確認と USE_FIXTURES=true への復帰★

  • ★read_console_messages でブラウザ側のエラー0件★(★使えなければ「確認できなかった」と記録★)
  • ★preview_logs でサーバ側のエラーを確認★(★A Node.js module is loaded が出ていないこと★)
  • ★★.env.local を USE_FIXTURES=true に戻し、dev サーバを再起動する★★
  • ★全画面が従来どおり動くことを確認する★(★特に /alerts で fixtures の8件+境界値1件が 正しいラベルで出ること★)。★これを確認して初めて「壊していない」と言えます★

この段階の完了条件

  • ★4-2 の8項目をすべて実測した★
  • ★4-3 の「◯分前異常」を実測した★
  • ★確認できなかった項目があれば、その理由と代替手段を控えた★
  • ★★USE_FIXTURES=true に戻して全画面が従来どおり動くことを確認した★★

段階5 — 記録と push

docs/decisions.md に追記

★着手直前に grep -nE "^### D-(0|1)[0-9][0-9]" docs/decisions.md | tail -3 で最終番号を実測すること★ (★^### D-0 だけでは3桁台を拾えない。^### D-1 も併せて grep すること★。 作成時点の最終は D-101)。

★記録すること★:

  1. ★★severity の新しい設計★★ — ★2値化 + precheck_minutes を採った理由(D-093 項2-2 の案A)★ / ★precheck コード → 分数の対応をどこに閉じ込めたか★ / ★D-091 項4 の切替後にどう変わるか★
  2. ★★PrecheckItem.id を string にしたこと★★(D-090 項7 / D-070 1-2 の実装完了)と、 ★影響が出た箇所の一覧★(★特に PathId を変えずインライン化した判断★)
  3. ★★detected_at に errored_at / created_at のどちらを使ったか★★と理由
  4. ★★battery_level を null にした判断★★(D-090 項8 の解決)と、 ★「事前チェック時点の残量」を取るには何が必要か★(★TeamWorks への照会候補として残す★)
  5. ★★launch_failed の DB 表現をどう決めたか★★(★B案で確定。D-103。D-090 項6 の解決★)
  6. ★★D-090 項6 / 項7 / 項8 の解決状況★★(★3件とも解決したか、残ったものがあるか★)
  7. ★i18n に足した対訳★(★"出発失敗" 等がこれまで対訳無しだった事実も記録★)
  8. ★段階2-4 の判断(robot_status を生値に寄せたか)★と理由
  9. ★シードに足したもの★(status / comment / 4カラム / 本清掃行のエラー行)
  10. ★実データで確認すべきこととして残るもの★ — ★precheck が 0/1/2 → 0/60/30 に切り替わる時期と 新旧混在の有無(D-091 項4)★ / ★PRECHECK_MINUTES_BEFORE と DB の precheck のどちらが正か (D-093 項3 の未決2)★ / ★precheck に入りうる値の範囲(D-093 項3 の第7回候補)★ / ★error_type の実値(D-090 項18)★
  11. ★段階4 で確認できなかった項目があれば、その理由と限界★
  12. ★第5歩に残したもの★(サマリ系4本)と ★実測値★ (npm test のファイル数・テスト数 / tsc / lint。★着手前の基準値との差★)

docs/handoff.md に追記

★最新は 追記29(docs/handoff.md:2342)★。次は ★追記30★ の見込み。★着手直前に自分で確認すること★。

push とこの段階の完了条件

  • ★git add は必ずファイルパスを明示。git add -A は禁止★(禁止事項13)
  • ★.env.local をコミットしないこと★(禁止事項14)。★USE_FIXTURES=true に戻したことを確認してから★
  • git push origin develop(★main へは push しない★)
  • ★完了条件★: docs/decisions.md に決定番号を追記した(★上記12項目すべて記録★)/ docs/handoff.md に追記した / ★origin/develop へ push 済み★ / ★文書末尾の「実行状態」表を更新した★

★やらないこと(第5歩以降)★

対象 いつ
★reportKpis / facilityPerformance / errorBreakdown / continuousProblems★ ★第5歩★(★サマリテーブル+当期その場集計の共通構造でまとめて★)
★dsAlerts の security_off 側★ ★D-091 項3 の内部調整の結果待ち★
★PATCH /prechecks/{id}/status の API ルート実装★ ★別の指示書★(★契約は直すが実装しない★)
★点検記録2本(inspections / inspectionDetail)★ ★最後★(D-072 項7)
★robot_status への formatRawLabel() 適用(D-089 項7 の申し送り)★ ★別の指示書★

完了条件(全体)

  1. ★着手前確認1件(launch_failed の DB 表現)は D-103 でB案に解決済み。新たな回答を待たずに段階0 から通しで実行した★
  2. 段階0〜5 をすべて実施した(★途中で止めた場合は、どこで止めたかを実行状態表に書くこと★)
  3. npx tsc --noEmit エラー0 / npm run lint 0 / npm test 全パス
  4. ★★d-watch-api.yaml の enum が [precheck, launch_failed] になり、precheck_minutes / facility_id / battery_level が定義され、id が string になっていること★★
  5. ★★components/parameters/PathId に差分が無いこと★★(他6箇所への巻き込みを避けた)
  6. ★USE_FIXTURES=false で /alerts が 200 で表示され、「◯分前異常」が正しく出ること★
  7. ★★precheck_minutes: 20 の fixtures 行が「20分前異常」と表示されること★★(★任意の分数に対応できた証明★)
  8. ★robot_status に英語識別子または Standby. が出ること(生値方針の帰結)★
  9. ★id が文字列で返り、ソート(D-052)が壊れていないこと★
  10. ★USE_FIXTURES=true に戻して全画面が従来どおり動くこと★
  11. ★prisma/schema.prisma / app/api/prechecks/route.ts / prismaSource の既存5メソッド / lib/db/prisma.ts / local-guard.ts / use-fixtures.ts / ds-status.ts / ★サマリ系4本(notImplemented のまま)★ に差分が無いこと★
  12. docs/decisions.md / docs/handoff.md の更新 / ★origin/develop へ push 済み★ / 文書末尾の「実行状態」表を更新した

実行状態

段階 状態 コミット
★着手前の確認★ ★★なし(すべて解決済み。2026-09-10)★★ — ★判定 = B案★(launch_failed = ★本清掃行に紐づく clean_status_histories の行が無い★。cleanLogs() の departed と同じ判定を共有する)。★記録: docs/decisions.md の D-103(:5222)★ / ★出発失敗の行の埋め方(id / robot_status / resolution_status / note / responded_at / resolved_at)= D-104★。★★新しい仕様判断は1つも要らない★★ —
段階0 前提の確認 ★実施済み★ コミットなし
段階1 API契約と型 ★実施済み★ ★0af0722★
段階2 fixtures と画面 ★実施済み★ ★8cdd299★
段階3 prismaSource.prechecks() ★実施済み★ ★cbfffc9★
段階4 動作確認 ★実施済み(★Browser pane が使えず curl で代替。目視できなかった項目は D-105 項12 に記録★)★ コミットなし
段階5 記録と push ★実施済み★ ★D-105 / 追記30 のコミット★

実行日: ★2026-09-10★ / 取得した決定番号: ★D-105★(★着手直前に grep -nE "^### D-(0\|1)[0-9][0-9]" docs/decisions.md \| tail -3 で最終番号 D-104 を実測してから採番。ずれは無かった★) 実測値(着手前 / 完了後): ★41ファイル / 778テスト / tsc 0 / lint 0★ → ★43ファイル / 801テスト / tsc 0 / lint 0★ ★決めたこと★: launch_failed の DB 表現 = ★B案で確定済み(D-103)★ / 出発失敗の行の埋め方 = ★D-104 で確定済み★(id = 本清掃行の clean_timetables.id / robot_status = Standby. / resolution_status = unresolved / note・responded_at・resolved_at = null)/ detected_at のカラム = ★errored_at ?? created_at★(出発失敗の行は ★scheduled_at と同値★。fixtures の前例に揃えた) / battery_level の扱い = ★常に null★(D-090 項8 の解決) / 段階2-4(robot_status を生値に寄せたか) = ★★寄せない(日本語のまま)★★ — ★lib/fixtures/clean-logs.ts が日本語のままで、シードがその値を error_type にそのまま入れているため、片方だけ英語にすると逆に食い違う★ / 変換ロジックの置き場所 = ★lib/db/precheck-mapping.ts を新設★ / clean_timetable_id が null のエラー行 = ★対象外★ / 本清掃行を指すエラー行 = ★対象外(launch_failed に読み替えない)★ 詳細: docs/decisions.md の該当番号を参照